ULedger Virtual Machine
La ULedger Virtual Machine (ULVM) permite escribir smart contracts. Los contratos se compilan a WebAssembly (WASM), y la ULVM expone un pequeño conjunto de funciones host que el contrato puede invocar para:
- leer/escribir el estado del blockchain (almacenamiento clave–valor)
- registrar mensajes
- emitir eventos
- acceder al contexto del contrato (llamante, propietario, altura de bloque)
Modelo de estado: almacenamiento clave–valor
ULedger almacena el estado del contrato como un mapa clave–valor en el Blockchain. El contrato lee y escribe valores por clave.
Conceptualmente, el estado del contrato se ve así:
{
"state": {
"key1": "value1",
"key2": "value2"
}
}
Entonces, cuando se llama a set_storage("my-key", someValue).
Tipos de datos soportados
Dado que el estado se serializa, la ULVM soporta un conjunto fijo de tipos (a través del serializador que codifica/decodifica valores):
Null = 0,
Bool = 1,
Int32 = 2,
Int64 = 3,
String = 4,
Bytes = 5,
Array = 6,
Map = 7,
Los métodos auxiliares (como
storeInt,retrieveInt, etc.) utilizan este serializador internamente para evitar codificar/decodificar bytes manualmente.
Funciones host de la ULVM (imports)
En AssemblyScript, las funciones host de la ULVM son (a alto nivel):
get_storage: obtiene los bytes del valor para una claveset_storage: almacena bytes de valor bajo una clavedelete_storage: elimina una clave del estadohas_key: verifica si existe una clavelog: escribe un mensaje de registroemit_event: emite un evento con un nombre y un payload de datosget_caller: la cuenta/billetera que está llamando a este contratoget_owner: el propietario del contratoget_block_height: altura de bloque actual
Estas funciones toman punteros (
usize) porque WASM pasa datos a través de memoria lineal. En la práctica, normalmente se llama a utilidades envolventes (comostoreInt) que gestionan punteros y serialización.
Por qué tipado fuerte (AssemblyScript)
Los contratos se escriben en AssemblyScript (sintaxis similar a TypeScript compilada a WASM). Debido a que compila a WebAssembly, se usan tipos explícitos como i32, i64, bool, Array<i32>, etc.
Tutorial: Almacenar y recuperar (ejemplo básico)
Este primer ejemplo muestra el flujo más sencillo:
- almacenar un
i32bajo una clave - recuperarlo
Paso 1: importar los helpers de la ULVM
import {
storeInt,
retrieveInt,
storeIntArray,
retrieveIntArray,
} from "../src/ulvm";
Estos helpers son envoltorios delgados alrededor de env.get_storage / env.set_storage, más la serialización.
Paso 2: escribir el contrato
// The entry file of your WebAssembly module.
import {
storeInt,
retrieveInt,
storeIntArray,
retrieveIntArray,
} from "../src/ulvm";
/**
* Simple direct test function that stores a value and retrieves it.
* Uses a fixed key to keep the example minimal.
*/
export function testStoreAndRetrieve(value: i32): i32 {
const key = "test-key";
// Store the value
storeValue(key, value);
// Retrieve and return the value
return retrieveValue(key);
}
/**
* Store a value in storage with a string key
*/
export function storeValue(key: string, value: i32): void {
storeInt(key, value);
}
/**
* Retrieve a value from storage by string key
*/
export function retrieveValue(key: string): i32 {
return retrieveInt(key);
}
/**
* Test storing and retrieving an array
*/
export function testStoreAndRetrieveArray(): i32 {
const key = "array-key";
const array = [10, 20, 30, 40, 50];
storeArray(key, array);
const retrieved = retrieveArray(key);
// Return the length to verify it worked
return retrieved.length;
}
/**
* Store an array with a string key
*/
export function storeArray(key: string, values: Array<i32>): void {
storeIntArray(key, values);
}
/**
* Retrieve an array by string key
*/
export function retrieveArray(key: string): Array<i32> {
return retrieveIntArray(key);
}
Qué hace este contrato
testStoreAndRetrieve(value: i32) -> i32
- escribe
valueen el almacenamiento bajo"test-key" - lee
"test-key"de vuelta - devuelve el valor recuperado
Entonces, si se llama a testStoreAndRetrieve(123), el estado termina conceptualmente así:
{
"state": {
"test-key": 123
}
}
testStoreAndRetrieveArray() -> i32
- almacena
[10,20,30,40,50]bajo"array-key" - lo recupera de vuelta
- devuelve la longitud del array (
5)
Compilación: compilar AssemblyScript a WebAssembly
Si se usa la estructura de proyecto estándar de AssemblyScript, el flujo típico es:
1) Instalar AssemblyScript (dependencia de desarrollo)
npm i -D assemblyscript
2) Colocar el punto de entrada del contrato en assembly/index.ts
Por ejemplo:
assembly/smart-contract.ts(el archivo del contrato)
3) Compilar a .wat y .wasm
Opción A: comando directo asc
npx asc assembly/smart-contract.ts -o smart-contract.wasm --textFile smart-contract.wat
Resultado: se obtiene un archivo .wat (ejemplo: smart-contract.wat) que es un archivo de texto que el SDK de ULedger puede desplegar.
Ejecución: Despliegue
Para desplegar el Smart Contract compilado se puede usar el SDK de ULedger.
Para la guía en Go sobre cómo hacer esto, ver la documentación de Despliegue de Smart Contracts.